class: center, middle, title-slide # The extendr project: Integrating R and Rust ### Claus O. Wilke ### last updated: 2021-04-20 --- background-image: url("data:image/png;base64,#resources/pexels-rene-asmussen-3990359.jpg") background-size: cover class: center middle <style type="text/css"> .headline-text h1 { text-shadow: 3px 0px 6px #fff, 0px -3px 6px #fff, 0px 3px 6px #fff, -3px 0px 6px #fff; color: black; font-size: 3.8em; font-weight: bold; } </style> .headline-text[ # How did I get myself<br>into this mess? ] ??? [Photo by Rene Asmussen from Pexels](https://www.pexels.com/photo/house-renovation-3990359/) --- ## ggtext: Styling of ggplot text elements with inline CSS -- .xtiny-font.pull-left[ ```r library(ggtext) colors <- c( Adelie = "#FFAF6F", Chinstrap = "#B455F3", Gentoo = "#4CA4A4" ) penguins %>% mutate( style = glue("color: {colors[species]};"), facet_label = glue( "<strong style='{style}'>{species}</strong>" )) %>% ggplot() + aes(x = flipper_length_mm, y = body_mass_g) + geom_point(aes(color = species)) + scale_color_manual( values = colors, guide = "none" ) + facet_wrap(~facet_label) + theme_bw() + theme( strip.text = element_markdown(size = rel(1)), strip.background = element_blank() ) ``` ] -- .xtiny-font.pull-right[ <!-- --> https://wilkelab.org/ggtext/ ] --- ## But I really want to be able to use CSS selectors -- .xtiny-font.pull-left[ ```r css <- " .Adelie { background-color: #FFAF6F; color: #303030; } .Chinstrap { background-color: #B455F3; color: #F0F0F0; } .Gentoo { background-color: #4CA4A4; color: #F0F0F0; } p { text-align: center; padding-top: 2px; font-weight: bold;}" ``` ] -- .xtiny-font.pull-right[ ```r penguins %>% mutate( facet_label = glue( "<p class = '{species}'>{species}</p>" )) %>% ggplot() + aes(x = flipper_length_mm, y = body_mass_g) + geom_point(aes(color = species)) + scale_color_manual( values = colors, guide = "none" ) + facet_wrap(~facet_label) + theme_bw() + theme( strip.text = element_html( css = css, size = rel(1) ), strip.background = element_blank() ) ``` https://clauswilke.com/sinab/ ] --- ## But I really want to be able to use CSS selectors .xtiny-font.pull-left[ ```r css <- " .Adelie { background-color: #FFAF6F; color: #303030; } .Chinstrap { background-color: #B455F3; color: #F0F0F0; } .Gentoo { background-color: #4CA4A4; color: #F0F0F0; } p { text-align: center; padding-top: 2px; font-weight: bold;}" ``` <!-- --> ] .xtiny-font.pull-right[ ```r penguins %>% mutate( facet_label = glue( "<p class = '{species}'>{species}</p>" )) %>% ggplot() + aes(x = flipper_length_mm, y = body_mass_g) + geom_point(aes(color = species)) + scale_color_manual( values = colors, guide = "none" ) + facet_wrap(~facet_label) + theme_bw() + theme( strip.text = element_html( css = css, size = rel(1) ), strip.background = element_blank() ) ``` https://clauswilke.com/sinab/ ] --- background-image: url("data:image/png;base64,#resources/servo.png") background-position: center background-size: contain --- ## Benefits of Rust -- - Compiled, performance of C -- - Compiler protects against undefined behavior -- - Modern abstractions: generics, containers, functional programming -- - Powerful macro programming language -- - Integrated build environment, dependency management -- - Built-in framework for unit tests -- - Built-in framework for documentation and distribution -- ## but ... --- background-image: url("data:image/png;base64,#resources/tommy-lisbin-_37crDvsAqE-unsplash.jpg") background-position: right background-size: contain # <br>Learning Rust can be<br>a steep climb ??? Photo credit: [Tommy Lisbin](https://unsplash.com/@tlisbin) https://unsplash.com/photos/_37crDvsAqE --- background-image: url("data:image/png;base64,#resources/code-screenshot.png") background-position: right background-size: 65% # <br>It's just like C++? --- ## A simple Rust program: Multiples of 3 and 5 .tiny-font[ ```rust fn main() { let x = 1..; // all integers from 1 to infininity let y:Vec<_> = x .into_iter() // convert to iterator .filter( // filter out numbers divisible by 3 or 5 |z| z % 3 == 0 || z % 5 == 0 ) .take(10) // keep the first 10 .collect(); // collect into vector println!("{:?}", y); //println!("{:?}", x); } ``` ] Try it on the [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ae012e466928d5adbb68b86ac7ffb43f) --- ## Fizzbuzz in Rust .tiny-font[ ```rust fn buzz(i: i32) -> (i32, Option<&'static str>) { match (i % 3, i % 5) { (0, 0) => (i, Some("FizzBuzz")), (0, _) => (i, Some("Fizz")), (_, 0) => (i, Some("Buzz")), (_, _) => (i, None), } } fn main() { for i in 1..16 { println!("{:?}", buzz(i)); } } ``` ] Try it on the [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=00850f854bfb5a8248a9b506358662a2) --- ## Combining the two previous examples .xtiny-font[ ```rust fn buzz(i: i32) -> (i32, Option<&'static str>) { match (i % 3, i % 5) { (0, 0) => (i, Some("FizzBuzz")), (0, _) => (i, Some("Fizz")), (_, 0) => (i, Some("Buzz")), (_, _) => (i, None), } } fn main() { let y: Vec<_> = (1..) .into_iter() // convert to iterator .map(buzz) // run `buzz()` over each integer .filter( // retain interesting cases |z| z.1.is_some(), ) .take(10) // keep the first 10 .collect(); // collect into vector println!("{:?}", y); } ``` ] Try it on the [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0984382148c0680b31bdf024981c0e7f) --- class: top center background-image: url("data:image/png;base64,#resources/christina-wocintechchat-com-HocFQHhGjDE-unsplash.jpg") background-size: cover background-position: middle <style type="text/css"> .text-on-image h2 { text-shadow: 1px 0px 4px #fff, 0px -1px 4px #fff, 0px 1px 4px #fff, -1px 0px 4px #fff; color: black; } </style> .text-on-image[ ## <br><br><br>How do we get Rust and R to talk to each other? ] ??? Photo credit: [Christina @ wocintechchat.com](https://unsplash.com/@wocintechchat) https://unsplash.com/photos/HocFQHhGjDE --- ## The extendr project: Rust crate and R package -- <img src = "data:image/png;base64,#resources/extendr-logo.svg", width = 20%, style = "vertical-align: middle;" /> **extendr:** Rust crate providing bindings to R -- <img src = "data:image/png;base64,#resources/rextendr-hexlogo.svg", width = 15%, style = "vertical-align: middle; margin-left: 2.5%; margin-right: 2.5%;" /> **rextendr:** R package providing support for the extendr crate ??? The extendr logo and hexlogo are distributed under the terms of the [Creative Commons Attribution-ShareAlike 4.0 International license](https://creativecommons.org/licenses/by-sa/4.0/) (CC-BY-SA 4.0). [The R logo](https://www.r-project.org/logo/) by The R Foundation is licensed under [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). [The Rust logo](https://github.com/rust-lang/rust-artwork) by Rust Foundation is licensed under [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/). --- ## Partners in crime <img src = "data:image/png;base64,#https://avatars.githubusercontent.com/u/1556316", width = "15%" /> [Andy Thomason](https://github.com/andy-thomason) -- <img src = "data:image/png;base64,#https://avatars.githubusercontent.com/u/8782986", width = "10%" /> [Ilia Kosenkov](https://github.com/Ilia-Kosenkov) <img src = "data:image/png;base64,#https://avatars.githubusercontent.com/u/1978793", width = "10%", style = "margin-left: .5in;"/> [Hiroaki Yutani](https://github.com/yutannihilation) <img src = "https://avatars.githubusercontent.com/u/4706822 ", width = "10%", style = "margin-left: .5in;"/> [Daniel Falbel](https://github.com/dfalbel) and [several others](https://github.com/extendr/extendr/graphs/contributors) --- <style> .move-down { margin-top: 9em; } </style> .move-down[ ## You don't need **rextendr** to write R packages using Rust ] -- (But it'll make your life easier; think of it as **devtools** for **extendr**) --- ## Calling Rust code from R: functions -- .pull-left[ **Rust code** .tiny-font[ ```rust use extendr_api::prelude::*; #[extendr] fn add_float(x: f64, y: f64) -> f64 { x + y } ``` ]] -- .pull-right[ **R code** .tiny-font[ ```r add_float(34.5, 64.2) ``` ``` [1] 98.7 ``` ]] --- ## Calling Rust code from R: objects -- .pull-left[ **Rust code** .tiny-font[ ```rust struct Counter { n: i32, } #[extendr] impl Counter { fn new() -> Self { Self { n: 0 } } fn increment(&mut self) { self.n += 1; } fn get(&self) -> i32 { self.n } } ``` ]] -- .pull-right[ **R code** .tiny-font[ ```r x <- Counter$new() x$get() ``` ``` [1] 0 ``` ```r x$increment() x$increment() x$get() ``` ``` [1] 2 ``` ]] --- ## Calling R code from Rust -- .tiny-font[ ```rust let data = R!(tibble::tibble(x = 1:5, y = 6:10))?; data ``` ``` # A tibble: 5 x 2 x y <int> <int> 1 1 6 2 2 7 3 3 8 4 4 9 5 5 10 ``` ] .absolute-bottom-right.small-font[ Note: Valid for extendr 0.2. API will change somewhat with the next extendr release. ] --- ## Calling R code from Rust .tiny-font[ ```rust let data = R!(tibble::tibble(x = 1:5, y = 6:10))?; rprintln!("Contents of data: {:?}", data); ``` ``` Contents of data: r!(List([r!([1, 2, 3, 4, 5]), r!([6, 7, 8, 9, 10])])) ``` ] .absolute-bottom-right.small-font[ Note: Valid for extendr 0.2. API will change somewhat with the next extendr release. ] --- ## Calling R code from Rust .tiny-font[ ```rust let data = R!(tibble::tibble(x = 1:5, y = 6:10))?; let iter = data.as_named_list_iter().ok_or("expected a list")?; rprintln!("Contents of iter: {:?}", iter); ``` ``` Contents of iter: Zip { a: ["x", "y"], b: [r!([1, 2, 3, 4, 5]), r!([6, 7, 8, 9, 10])] } ``` ] .absolute-bottom-right.small-font[ Note: Valid for extendr 0.2. API will change somewhat with the next extendr release. ] --- ## Calling R code from Rust .tiny-font[ ```rust let data = R!(tibble::tibble(x = 1:5, y = 6:10))?; let iter = data.as_named_list_iter().ok_or("expected a list")?; rprintln!("Individual data columns:"); for column in iter { rprintln!("Column {:}: {:?}", column.0, column.1); }; ``` ``` Individual data columns: Column x: r!([1, 2, 3, 4, 5]) Column y: r!([6, 7, 8, 9, 10]) ``` ] .absolute-bottom-right.small-font[ Note: Valid for extendr 0.2. API will change somewhat with the next extendr release. ] --- ## Explore Rust code from an R session with **rextendr** -- .tiny-font[ ```r library(rextendr) # create a Rust function rust_function("fn add(a:f64, b:f64) -> f64 { a + b }") # call it from R add(2.5, 4.7) ``` ``` [1] 7.2 ``` ] --- ## Embed compiled Rust code in your RMarkdown -- .pull-left[ **RMarkdown input** .tiny-font[ ````markdown ```{extendr} let x = 5; let y = 7; let z = x*y; assert_eq!(z, 35); ``` ```` ]] -- .pull-right[ **Rendered output** .tiny-font[ ```rust let x = 5; let y = 7; let z = x*y; assert_eq!(z, 35); ``` ]] -- How do we know this actually got compiled and run? --- ## Embed compiled Rust code in your RMarkdown .pull-left[ **RMarkdown input** .tiny-font[ ````markdown ```{extendr} let x = 5; let y = 7; let z = x*y; assert_eq!(z, 35); // No semicolon, // value is returned to R z ``` ```` ]] .pull-right[ **Rendered output** .tiny-font[ ```rust let x = 5; let y = 7; let z = x*y; assert_eq!(z, 35); // No semicolon, // value is returned to R z ``` ``` [1] 35 ``` ]] --- ## Chaining multiple Rust chunks together -- .pull-left[ **RMarkdown input** .tiny-font[ ````markdown Define variable `x`: ```{extendr A, eval = FALSE} let x = 1; ``` Define variable `y`: ```{extendr B, eval = FALSE} let y = 2; ``` Print: ```{extendr out, preamble = c("A", "B")} rprintln!("x = {}, y = {}", x, y); ``` ```` ]] -- .pull-right[ **Rendered output** .tiny-font[ Define variable `x`: ```rust let x = 1; ``` Define variable `y`: ```rust let y = 2; ``` Print: ```rust rprintln!("x = {}, y = {}", x, y); ``` ``` x = 1, y = 2 ``` ]] --- background-image: url("data:image/png;base64,#https://images.pexels.com/photos/37730/sunset-boat-sea-ship-37730.jpeg") background-size: cover class: center <style type="text/css"> .doublespacing { line-height: 1.5; } .fill-to-right { display: inline-block; padding-left: 17.4em; } </style> .doublespacing.text-on-image[ ## “Most people overestimate what they can do in one year and underestimate what they can do in ten years.”<br><span class = "fill-to-right">— Bill Gates<span> ] ??? Photo source here https://www.pexels.com/photo/sunset-ship-boat-sea-37730/ --- ## Resources - **extendr** - [Release on crates.io](https://crates.io/crates/extendr-api) - [Documentation (released version)](https://docs.rs/extendr-api/) - [Documentation (in development)](https://extendr.github.io/extendr/extendr_api/) - [Source repository](https://github.com/extendr/extendr) - **rextendr** - Release on CRAN: coming soon - [Pakackge website](https://extendr.github.io/rextendr/) - [Source repository](https://github.com/extendr/rextendr)